import UIKit

class DWSplitDownloadOperation: Operation {
    var progressBlock: ((Int64, Int64) -> ())?
    private var currentBytes: Int64 = 0
    private var endBytes: Int64 = 0
    private (set) var error: DWSplitDownloadError?
    private var url: URL!
    private var path: String!
    private var session: URLSession!
    private var task: URLSessionDataTask!
    private var outputStream: OutputStream?
    private var taskFinished: Bool = true {
        willSet {
            if taskFinished != newValue {
                willChangeValue(forKey: "isFinished")
            }
        }
        didSet {
            if taskFinished != oldValue {
                didChangeValue(forKey: "isFinished")
            }
        }
    }
    private var taskExecuting: Bool = false {
        willSet {
            if taskExecuting != newValue {
                willChangeValue(forKey: "isExecuting")
            }
        }
        didSet {
            if taskExecuting != oldValue {
                didChangeValue(forKey: "isExecuting")
            }
        }
    }
    override var isFinished: Bool {
        return taskFinished
    }
    override var isExecuting: Bool {
        return taskExecuting
    }
    override var isAsynchronous: Bool {
        return true
    }
        
    init(url: URL, path: String, currentBytes: Int64, endBytes: Int64) {
        super.init()
        self.url = url
        self.path = path
        var request = URLRequest(url: url)
        request.timeoutInterval = 300
        
        if let fileInfo = try? FileManager.default.attributesOfItem(atPath: path), let fileSize = fileInfo[.size] as? Int64 {
            self.currentBytes = currentBytes + fileSize
        }else {
            self.currentBytes = currentBytes
        }
        self.endBytes = endBytes
        let requestRange = String(format: "bytes=%llu-%llu", self.currentBytes, endBytes)
        request.addValue(requestRange, forHTTPHeaderField: "Range")
        session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
        task = session.dataTask(with: request)
    }
    
}

extension DWSplitDownloadOperation {
    override func start() {
        autoreleasepool {
            if isCancelled || self.currentBytes >= self.endBytes {
                taskFinished = true
                taskExecuting = false
            }else {
                taskFinished = false
                taskExecuting = true
                startTask()
            }
        }
    }
    override func cancel() {
        task.cancel()
        super.cancel()
    }
    
}
private extension DWSplitDownloadOperation {
    func startTask() {
        task.resume()
    }
    func complete(_ error: DWSplitDownloadError? = nil) {
        self.error = error
//        currentData = Data.init()
        outputStream?.close()
        outputStream = nil
        taskFinished = true
        taskExecuting = false
    }
}
extension DWSplitDownloadOperation: URLSessionDataDelegate {
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
        if !isCancelled {
            guard let response = dataTask.response as? HTTPURLResponse else {
                complete(.notHTTPURLResponse)
                return
            }
            guard response.statusCode == 200 || response.statusCode == 206 else {
                complete(.statusCode(response.statusCode))
                return
            }
            if response.statusCode == 200,
               FileManager.default.fileExists(atPath: path) {
                do {
                    try FileManager.default.removeItem(atPath: path)
                } catch  {
                    complete(.throws(error))
                    return
                }
            }
            completionHandler(.allow)
        }
        outputStream = OutputStream(url: URL(fileURLWithPath: path), append: true)
        outputStream?.open()
    }
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        session.invalidateAndCancel()
        guard let response = task.response as? HTTPURLResponse else {
            complete(.notHTTPURLResponse)
            return
        }
        if let error = error {
            complete(.download(error))
        }else if (response.statusCode == 200 || response.statusCode == 206) {
            complete()
        }else {
            complete(.statusCode(response.statusCode))
        }
    }
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        if !isCancelled {
            let receiveBytes = dataTask.countOfBytesReceived + currentBytes
//            currentData.append(data)
//            do {
//                try currentData.write(to: URL.init(fileURLWithPath: path))
//            } catch {
//                complete(.fileCompound)
//            }
            outputStream?.write(Array(data), maxLength: data.count)
            DispatchQueue.main.async {[weak self]in
                guard let strongSelf = self else { return }
                strongSelf.progressBlock?(receiveBytes, Int64(data.count))
            }
        }
    }
}
